<?php

/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/3/1 0001
 * Time: 21:20
 */

class SplDoublyLinkedList2 implements Iterator, ArrayAccess, Countable
{
    const IT_MODE_LIFO = 2;
    const IT_MODE_FIFO = 0;
    const IT_MODE_DELETE = 1;
    const IT_MODE_KEEP = 0;
    /**
     * @var array 定义一个数组用于存放数据
     */
    protected $_list = array();
    /**
     * @var int 链表的迭代模式
     */
    protected $_it_mode = 0;

    /**
     * @var int 链表指针
     */
    protected $_it_pos = 0;

    /**
     * @return mixed 返回被移除尾部及节点的元素
     */
    public function pop()
    {
        if (count($this->_list) == 0) {
            throw new RuntimeException('不能从空白处弹出来 ');
        }
        return array_pop($this->_list);
    }

    public function shift()
    {
        if (count($this->_list) == 0) {
            throw new RuntimeException('不能从空白处转移');
        }
        return array_shift($this->_list);
    }

    /**
     * 往链表尾部添加一个元素
     * @param $data
     * @return bool
     */
    public function push($data)
    {
        array_push($this->_list, $data);
        return true;
    }

    /**
     * 往链表头部添加一个节点元素
     * @param $data
     * @return bool
     */
    public function unshift($data)
    {
        array_unshift($this->_list, $data);
        return true;
    }

    /**
     * 返回尾部节点元素，并把指针指向尾部节点元素
     * @return mixed
     */
    public function top()
    {
        return end($this->_list);
    }

    /**
     * 返回头部节点元素，并把指针指向头部节点元素
     * @return mixed
     */
    public function bottom()
    {
        return reset($this->_list);
    }

    /**
     * 返回链表节点数
     * @return int
     */
    public function count()
    {
        return count($this->_list);
    }

    public function isEmpty()
    {
        return ($this->count() == 0);
    }

    /**
     * 设置迭代模式
     * - 迭代的顺序 (先进先出、后进先出)
     *  - SplDoublyLnkedList::IT_MODE_LIFO (堆栈)
     *  - SplDoublyLnkedList::IT_MODE_FIFO (队列)
     *
     * - 迭代过程中迭代器的行为
     *  - SplDoublyLnkedList::IT_MODE_DELETE (删除已迭代的节点元素)
     *  - SplDoublyLnkedList::IT_MODE_KEEP   (保留已迭代的节点元素)
     *
     * 默认的模式是 0 : SplDoublyLnkedList::IT_MODE_FIFO | SplDoublyLnkedList::IT_MODE_KEEP
     *
     * @param $mode : 新的迭代模式
     *
     */
    public function setIteratorMode($mode)
    {
        $this->_it_mode = $mode;
    }

    /**
     * @return int 返回当前的迭代模式
     */
    public function getIteratorMode()
    {
        return $this->_it_mode;
    }

    public function rewind()
    {
        if ($this->_it_mode & self::IT_MODE_LIFO) {
            $this->_it_pos = count($this->_list) - 1;
        } else {
            $this->_it_pos = 0;
        }
    }

    /**
     * 判断指针对应的节点元素是否存在
     * @return bool
     */
    public function valid()
    {
        return array_key_exists($this->_it_pos, $this->_list);
    }

    /**
     * 返回当前指针的偏移位置
     * @return int|mixed
     */
    public function key()
    {
        return $this->_it_pos;
    }

    public function current()
    {
        return $this->_list[$this->_it_pos];
    }

    /**
     * 将指针向前移动一个偏移位置
     */
    public function next()
    {
        if ($this->_it_mode & self::IT_MODE_LIFO) { //堆栈结构 后进先出
            if ($this->_it_mode & self::IT_MODE_DELETE) {
                $this->pop();
            }
            $this->_it_pos--;
        } else { // 否者队列结构
            if ($this->_it_mode & self::IT_MODE_DELETE) { //先进先出
                $this->shift();
            }
            $this->_it_pos++;
        }
    }

    /**
     * 偏移位置是否存在
     * @param mixed $offset
     * @return bool
     */
    public function offsetExists($offset)
    {
        if (!is_numeric($offset)) {
            throw new OutOfRangeException('偏移无效或超出范围 ');
        }
        return array_key_exists($offset, $this->_list);
    }

    /**
     * 获取偏移位置对应的值 如果偏移位置超出范围或者无效则抛出异常
     * @param mixed $offset
     * @return mixed
     */
    public function offsetGet($offset)
    {
        if ($this->_it_mode & self::IT_MODE_LIFO) {
            $realOffset = $this->count() - $offset;
        } else {
            $realOffset = $offset;
        }
        if (!is_numeric($offset) || array_key_exists($realOffset, $this->_list)) {
            throw new OutOfRangeException('偏移无效或超出范围');
        }
        return $this->_list[$realOffset];
    }

    public function offsetSet($offset, $value)
    {
        if ($offset === null) {
            return $this->push($value);
        }
        if ($this->_it_mode & self::IT_MODE_LIFO) {
            $realOffset = $this->count() - $offset;
        } else {
            $realOffset = $offset;
        }
        if (!is_numeric($offset) || array_key_exists($realOffset, $this->_list)) {
            throw new OutOfRangeException('偏移无效或超出范围');
        }
        $this->_list[$realOffset] = $value;
    }

    public function offsetUnset($offset)
    {
        if ($this->_it_mode & self::IT_MODE_LIFO) {
            $realOffset = $this->count() - $offset;
        } else {
            $realOffset = $offset;
        }
        if (!is_numeric($offset) || array_key_exists($realOffset, $this->_list)) {
            throw new OutOfRangeException('偏移无效或超出范围');
        }
        array_splice($this->_list, $offset, 1);
    }
}

$doubly = new SplDoublyLinkedList2();
$doubly->push('a');
$doubly->push('b');
$doubly->push('c');
$doubly->push('d');
$doubly->push('f');
echo '初始链表';
var_dump($doubly);
//设置先进先出Keep模式迭代输出
$doubly->setIteratorMode(SplDoublyLinkedList2::IT_MODE_LIFO | SplDoublyLinkedList2::IT_MODE_KEEP);
$doubly->rewind();
foreach ($doubly as $index => $item) {
//    echo $index . '=' . $item . "\n\r";
}
//4=f
//3=d
//2=c
//1=b
//0=a
//设置先进先出Keep模式迭代输出
$doubly->setIteratorMode(SplDoublyLinkedList2::IT_MODE_FIFO | SplDoublyLinkedList2::IT_MODE_KEEP);
$doubly->rewind();
foreach ($doubly as $index => $item) {
//    echo $index . '=' . $item . "\n\r";
}
//0=a
//1=b
//2=c
//3=d
//4=f
//后进先出Delete模式迭代输出
$doubly->setIteratorMode(SplDoublyLinkedList2::IT_MODE_LIFO | SplDoublyLinkedList2::IT_MODE_DELETE);
$doubly->rewind();
foreach ($doubly as $index => $item) {
    if ($index == 1) break;
    echo $index . '=' . $item . "\n\r";
}
//模式迭代之后的链表
var_dump($doubly);
//object(SplDoublyLinkedList2)#1 (3) {
//["_list":protected]=>
//  array(2) {
//    [0]=>
//    string(1) "a"
//    [1]=>
//    string(1) "b"
//  }

//SplDoublyLinkedList2::IT_MODE_FIFO | SplDoublyLinkedList2::IT_MODE_DELETE模式下输出
//object(SplDoublyLinkedList2)#1 (3) {
//["_list":protected]=>
//  array(4) {
//    [0]=>
//    string(1) "b"
//    [1]=>
//    string(1) "c"
//    [2]=>
//    string(1) "d"
//    [3]=>
//    string(1) "f"
//  }
